package fybug.nulll.pdstream.WR.Stream;
import com.sun.istack.internal.NotNull;
import com.sun.istack.internal.Nullable;

import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.nio.CharBuffer;

import fybug.nulll.pdfunctionlibrary.Processing.Check;
import fybug.nulll.pdfunctionlibrary.Processing.Err.Stop;
import fybug.nulll.pdfunctionlibrary.Util.Processing.Arrarys;
import fybug.nulll.pdstream.Interface.UpdateSize;
import fybug.nulll.pdstream.Source.StreamSource;
import fybug.nulll.pdstream.StreamOperator;
import fybug.nulll.pdstream.WR.Err.CharStreamErr;
import fybug.nulll.pdstream.WR.W_R;
/**
 * <h2>字符流复制器.</h2>
 * <pre>
 * <b>建议使用工场类 {@link W_R} 构造</b>
 *
 * 从绑定的 <b>操作源</b> 中获取的 <b>写入流</b> 和绑定 <b>被复制流</b> 进行复制
 * 支持 <b>多个被复制流</b> 复制到 <b>多个写入流</b>
 * 可传入 <b>不绑定在源中</b> 的要复制的被复制流
 * 可使用该操作器中的方法对操作源进行 <b>写入流和被复制流的重绑定</b>
 * 可以使用 <b>连续操作</b> ,有效解决不稳定流的处理问题</pre>
 * <pre>常规操作方法：
 * {@link #copy()}
 * {@link #copy(Reader...)}
 * </pre>
 *
 * @author fybug
 * @version 0.1.3
 * @see StreamOperator
 * @see Writer
 * @see Reader
 * @see W_R
 * @see CharStreamErr
 * @since JDK 1.8
 */
@SuppressWarnings( "all" )
public final
class CopyChar extends StreamOperator<Writer, Reader[]> {
    /** <p>构造一个复制器并绑定文本源.</p> */
    public
    CopyChar(@Nullable final StreamSource<Writer, Reader[]> r) {binSource(r);}

    /*
     * MainSource
     */

    /** <p>绑定文本源.</p> */
    @NotNull
    public final
    CopyChar binSource(@Nullable final StreamSource<Writer, Reader[]> r)
    {
        super.binSource(r);
        return this;
    }

    /** <p>重绑定操作源中的流.</p> */
    @NotNull
    public final
    CopyChar binCopyTo(@Nullable final Writer... r) {
        try {
            source().binStream(r);
        } catch ( Stop ignored ) {
        }
        return this;
    }

    /** <p>增加操作源中的流.</p> */
    @NotNull
    public final
    CopyChar appendCopyTo(final Writer... r) {
        try {
            @NotNull final StreamSource<Writer, Reader[]> source = source();
            Writer[] outputStreams;
            try {
                outputStreams = source.stream();
            } catch ( Stop e ) {
                outputStreams = null;
            }
            source.binStream(Arrarys.append(outputStreams, r));
        } catch ( Stop ignored ) {
        }
        return this;
    }

    /** <p>重绑定操作源中的被复制流.</p> */
    @NotNull
    public final
    CopyChar binCopyFrom(@Nullable final Reader... r) {
        try {
            source().binOutPut(r);
        } catch ( Stop ignored ) {
        }
        return this;
    }

    /** <p>增加操作源中的被复制流.</p> */
    @NotNull
    public final
    CopyChar appendCopyFrom(final Reader... r) {
        try {
            @NotNull final StreamSource<Writer, Reader[]> source = source();
            Reader[] outputStreams;
            try {
                outputStreams = source.getOutPut();
            } catch ( Stop e ) {
                outputStreams = null;
            }
            source.binOutPut(Arrarys.append(outputStreams, r));
        } catch ( Stop ignored ) {
        }
        return this;
    }

    /*
     * Copy
     */

    /**
     * <p>将绑定的被复制流中的字符输出到绑定的写入流中.</p>
     * <pre>
     * 从源中绑定的所有被复制流获取字符并输出到所有写入流中
     * 适用于所有流
     *
     * 但不建议使用该方法 <b>复制大量数据</b>
     * 会 <b>内存不足</b> 的
     * </pre>
     *
     * @throws CharStreamErr 复制过程出错
     */
    @NotNull
    public final
    CopyChar copy() throws CharStreamErr {
        try {
            copy(source().getOutPut());
        } catch ( Stop ignored ) {
        }
        return this;
    }

    /**
     * <p>将输入的被复制流中的字符输出到绑定的写入流中.</p>
     * <pre>
     * 从输入的所有被复制流获取字符并输出到所有写入流中
     * 适用于所有流
     *
     * 但不建议使用该方法 <b>复制大量数据</b>
     * 会 <b>内存不足</b> 的
     * </pre>
     *
     * @throws CharStreamErr 复制过程出错
     */
    @NotNull
    public final
    CopyChar copy(@Nullable Reader... r) throws CharStreamErr {
        @NotNull Writer[] w;
        try {
            w = source().stream();
            r = Check.checkNull(Arrarys.checkNullArray(Arrarys.trim(r)));
        } catch ( Stop e ) {
            return this;
        }
        @NotNull //* 使用字符缓冲区，降低修改开销 *//
                StringBuilder stringBuffer = new StringBuilder(); // 读取暂存区
        @NotNull Reader reader = null;
        @NotNull char[] charBuffer = new char[0];
        int c; // 当前读取
        try {
            synchronized ( w ){
                for ( int i = 0; i < r.length; i++ ){
                    /* 读取所有流 */
                    reader = r[i];
                    while( (c = reader.read()) > -1 )
                        /* 将该流的内容读取出来 */
                        stringBuffer.append((char) c);
                    c = 0;
                    if (stringBuffer.length() == 0)
                        continue;
                    // 转移到缓冲区
                    if (stringBuffer.length() != charBuffer.length)
                        charBuffer = new char[stringBuffer.length()];
                    stringBuffer.getChars(0, charBuffer.length, charBuffer, 0);
                    // 写入
                    forWrite(w, charBuffer, charBuffer.length);
                    stringBuffer.setLength(0);
                }
            }
            charBuffer = null;
        } catch ( IOException | CharStreamErr e ) {
            throw new CharStreamErr(e.getMessage(), reader,
                                    (e instanceof CharStreamErr) ? ((CharStreamErr) e).writer
                                            : null);
        }
        stringBuffer.trimToSize();
        stringBuffer = null;
        reader = null;
        flush(w);
        return this;
    }

    /**
     * <p>使用连续操作将绑定的被复制流中的字符复制到绑定的写入流中.</p>
     * <pre>
     * 按顺序从被复制流中读取字符，并放入写入流中
     * 每次复制一个片段
     * 每个片段都会写入所有写入流中
     * 内存消耗取决于片段大小
     * 适用于所有流
     * </pre>
     *
     * @param size 片段大小
     *
     * @throws CharStreamErr 复制出错
     */
    @NotNull
    public final
    CopyChar buffer(final int size) throws CharStreamErr
    { return dynamic(size, checkUpSize()); }

    /**
     * <p>使用连续操作将绑定的被复制流中的字符复制到绑定的写入流中.</p>
     * <pre>
     * 按顺序从被复制流中读取字符，并放入写入流中
     * 每次复制一个片段
     * 可以使用接口 <b>动态调整片段大小</b>
     * 每个片段都会写入所有写入流中
     * 内存消耗取决于片段大小
     * 将片段大小调整为{@code 0} 可中断复制
     * 适用与所有流
     * </pre>
     *
     * @param size 初始片段大小
     * @param see  监听接口
     *
     * @throws CharStreamErr 复制出错
     * @see UpdateSize
     */
    @NotNull
    public final
    CopyChar dynamic(final int size, @Nullable final UpdateSize see) throws CharStreamErr
    {
        @NotNull Writer[] w;
        @NotNull Reader[] r;
        try {
            w = source().stream();
            r = source().getOutPut();
            if (size <= 0)
                return this;
        } catch ( Stop e ) {
            return this;
        }
        @NotNull Reader reader = null;
        int readSize; // 当前读取数量
        try {
            synchronized ( w ){
                if (see != null)
                    see.init();
                @NotNull //* 使用 Nio 包的缓冲区,提高缓冲区复用效率 *//
                        CharBuffer charBuffer = CharBuffer.allocate(size); // 缓冲区
                for ( int i = 0; i < r.length; i++ ){
                    reader = r[i]; // 减少寻址
                    while( (readSize = reader.read(charBuffer)) > -1 ){
                        /* 读取所有流 */
                        if (readSize > 0) {
                            // 写入
                            forWrite(w, charBuffer.array(), readSize);
                            if (readSize < charBuffer.capacity())
                                /* 读取完成 */
                                break;
                        }
                        // 更新缓存大小
                        charBuffer = Buffer.upBufferSize(see, charBuffer, readSize);
                    }
                }
            }
            r = null;
            readSize = 0;
            reader = null;
            flush(w);
        } catch ( Stop ignored ) {
        } catch ( IOException | CharStreamErr e ) {
            throw new CharStreamErr(e.getMessage(), reader,
                                    e instanceof CharStreamErr ? ((CharStreamErr) e).writer : null);
        } finally {
            if (see != null)
                see.destroy();
        }
        return this;
    }

    /*
     * 流操作
     */

    // 集体刷新
    private
    void flush(@NotNull final Writer[] w) throws CharStreamErr {
        @Nullable Writer write = null;
        try {
            synchronized ( w ){
                for ( int i = 0; i < w.length; i++ ){
                    /* 刷新流 */
                    write = w[i];
                    write.flush();
                }
            }
        } catch ( IOException e ) {
            throw new CharStreamErr(e.getMessage(), null, write);
        }
    }

    private
    void forWrite(@NotNull final Writer[] w, @NotNull final char[] stringCahce, int lenght)
    throws CharStreamErr
    {
        @NotNull Writer writer = null;
        try {
            synchronized ( w ){
                for ( int i = 0; i < w.length; i++ ){
                    /* 写入所有 */
                    writer = w[i]; // 减少寻址
                    writer.write(stringCahce, 0, lenght);
                }
            }
        } catch ( IOException e ) {
            throw new CharStreamErr(e.getMessage(), null, writer);
        }
    }
}
